home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / misc / update / tpasc302.cpt / TCL Update / New ArtClass Files / CPaintPane.p < prev    next >
Encoding:
Text File  |  1990-05-11  |  14.7 KB  |  594 lines

  1. {****************************************************}
  2. {         CPaintPane.p}
  3. {}
  4. {        The PaintPane Class}
  5. {}
  6. {        A MacPaint-type pane which stores a bitmap image. The PaintPane has}
  7. {        its own offscreen image and port. The "real" image is stored offscreen}
  8. {        and CopyBits operations are performed to put the image on the screen.}
  9. {        A single ScratchPad BitMap, the same size as the paint image, is used}
  10. {        in order to have smooth drawing (no flashing). This one scratchpad is}
  11. {        shared by all PaintPanes (i.e, multiple windows). To implement undo,}
  12. {        another bitmap is created to store sufficient information to restore}
  13. {        the state of a painting. Each PaintPane has its own undo buffer.}
  14. {        With multiple windows, this means that the "Undo" command will undo}
  15. {        the last action for the active window.}
  16. {}
  17. {        The drawing tools are pretty simple: geometric shapes, pencil, brush,}
  18. {        text tool, and selection rectangle.}
  19. {}
  20. {        Geometric shapes are constrained to squares (or circles) if the}
  21. {        shift key is held down.}
  22. {}
  23. {        SUPERCLASS = CBitMapPane}
  24. {}
  25. {        Copyright ⌐ 1989, Symantec Corporation.  All rights reserved.            }
  26. {}
  27. {****************************************************}
  28.  
  29. unit CPaintPane;
  30.  
  31. interface
  32.  
  33.     uses
  34.         TCL, MoreTCL, ArtClassIntf;
  35.  
  36.     const
  37.         DELETE_KEY = 8;                    { Character code                    }
  38.         PAT_MARCH_ANTS = 300;        { Pattern for marching ants        }
  39.         iCUT = 6;                            { Index of task name for Cut        }
  40.         iCLEAR = 9;                        { Index of task name for Clear    }
  41.  
  42. implementation
  43.  
  44.  
  45.  
  46. {*** C O N S T R U C T I O N / D E S T R U C T I O N   M E T H O D S ***}
  47.  
  48.  
  49.  
  50. {****************************************************}
  51. { IPaintPane}
  52. {}
  53. {        Initialize a PaintPane object}
  54. {}
  55. {****************************************************}
  56.  
  57.     procedure CPaintPane.IPaintPane (anEnclosure: CView; aSupervisor: CBureaucrat; aWidth, aHeight, aHEncl, aVEncl: integer; aHSizing, aVSizing: SizingOption; aBounds: Rect; aBitMap: CBitMap);
  58.     begin
  59.         IBitMapPane(anEnclosure, aSupervisor, aWidth, aHeight, aHEncl, aVEncl, aHSizing, aVSizing, aBounds, aBitMap, TRUE);
  60.  
  61.         wantsClicks := TRUE;
  62.         SetRect(selRect, 0, 0, 0, 0);
  63.         lastTask := nil;
  64.     end;
  65.  
  66.  
  67. {****************************************************}
  68. { DoCommand (OVERRIDE)}
  69. {}
  70. {        Handle Edit commands}
  71. {}
  72. {****************************************************}
  73.  
  74.     procedure CPaintPane.DoCommand (theCommand: longint);
  75.     begin
  76.         if HiWord(-theCommand) = MENUtools then
  77.             begin
  78.                 KillSelRect;
  79.                 inherited DoCommand(theCommand);
  80.             end
  81.  
  82.         else
  83.             begin
  84.                 case theCommand of
  85.  
  86.                     cmdCut: 
  87.                         begin
  88.                             DoCopy;
  89.                             DoClear(iCUT);
  90.                         end;
  91.  
  92.                     cmdCopy: 
  93.                         DoCopy;
  94.  
  95.                     cmdPaste: 
  96.                         DoPaste;
  97.  
  98.                     cmdClear: 
  99.                         DoClear(iCLEAR);
  100.  
  101.                     otherwise
  102.                         inherited DoCommand(theCommand);
  103.                 end;
  104.             end;
  105.     end;
  106.  
  107.  
  108. {****************************************************}
  109. { UpdateMenus (OVERRIDE)}
  110. {}
  111. {        Enable appropriate Edit commands}
  112. {}
  113. {****************************************************}
  114.  
  115.     procedure CPaintPane.UpdateMenus;
  116.     begin
  117.         inherited UpdateMenus;
  118.  
  119.         if gGopher = SELF then
  120.             begin
  121.                 if not EmptyRect(selRect) then
  122.                     begin
  123.                         gBartender.EnableCmd(cmdCut);
  124.                         gBartender.EnableCmd(cmdCopy);
  125.                         gBartender.EnableCmd(cmdClear);
  126.                     end;
  127.  
  128.                 if gClipboard.DataSize('PICT') > 0 then
  129.                     gBartender.EnableCmd(cmdPaste);
  130.             end;
  131.     end;
  132.  
  133.  
  134. {****************************************************}
  135. { DoClick (OVERRIDE)}
  136. {}
  137. {        Mouse click inside the PaintPane}
  138. {}
  139. {****************************************************}
  140.  
  141.     procedure CPaintPane.DoClick (hitPt: Point; modifierKeys: integer; when: longint);
  142.         var
  143.             thePaintTask: CPaintTask;
  144.             theBounds: Rect;
  145.             select: Boolean;
  146.             notifiable: Boolean;
  147.             theLastTask: CPaintTask;        { Altered by TCL Weaver 1.0 (5/9/90) }
  148.  
  149.     begin
  150.         select := FALSE;
  151.         notifiable := TRUE;
  152.         if not PtInRect(hitPt, bounds) then
  153.             Exit(DoClick);
  154.  
  155.         if gPaintTool <> toolSELECT then     { Any new painting will clobber the    }
  156.             KillSelRect;                            {   selection                            }
  157.  
  158.         case gPaintTool of
  159.  
  160.             toolSELECT:                    { We could be selecting or dragging    }
  161.                 begin
  162.                     notifiable := FALSE;
  163.                     if PtInRect(hitPt, selRect) then
  164.                         begin
  165.                                     { Dragging the current selection        }
  166.  
  167.                             if (lastTask = nil) or not member(lastTask, CDragger) then
  168.                                 begin
  169.                                     { This is a brand new drag, NOT the    }
  170.                                     {   continuation of a previous one    }
  171.  
  172.                                     new(CDragger(theLastTask));     { Altered by TCL Weaver 1.0 (5/9/90) }
  173.                                     lastTask := theLastTask;
  174.                                     CDragger(lastTask).IDragger(SELF, itsBitMap);
  175.                                     notifiable := TRUE;
  176.                                 end;
  177.                             thePaintTask := lastTask;
  178.                         end
  179.  
  180.                     else
  181.                         begin                    { Make a selection rectangle            }
  182.                             select := TRUE;
  183.                             KillSelRect;
  184.                             new(CSelectionRect(thePaintTask));
  185.                             CSelectionRect(thePaintTask).ISelectionRect(SELF, itsBitMap);
  186.  
  187.                             gSleepTime := 0;        { Cursor shape needs to change within    }
  188.                                         {   selection, so we must force an        }
  189.                                         {   idle so that the mouse region will    }
  190.                                         {   be updated                                }
  191.                         end;
  192.                 end;
  193.  
  194.             toolBRUSH: 
  195.                 begin
  196.                     new(CToolBrush(thePaintTask));
  197.                     CToolBrush(thePaintTask).IToolBrush(SELF, itsBitMap);
  198.                 end;
  199.  
  200.             toolPENCIL: 
  201.                 begin
  202.                     new(CToolPencil(thePaintTask));
  203.                     CToolPencil(thePaintTask).IToolPencil(SELF, itsBitMap);
  204.                 end;
  205.  
  206.             toolERASER: 
  207.                 begin
  208.                     new(CToolEraser(thePaintTask));
  209.                     CToolEraser(thePaintTask).IToolEraser(SELF, itsBitMap);
  210.                 end;
  211.  
  212.             toolRECT: 
  213.                 begin
  214.                     new(CToolRect(thePaintTask));
  215.                     CToolRect(thePaintTask).IToolShapes(SELF, itsBitMap);
  216.                 end;
  217.  
  218.             toolFILLRECT: 
  219.                 begin
  220.                     new(CToolFillRect(thePaintTask));
  221.                     CToolFillRect(thePaintTask).IToolShapes(SELF, itsBitMap);
  222.                 end;
  223.  
  224.             toolTEXT: 
  225.                 begin
  226.                     new(CToolText(thePaintTask));
  227.                     CToolText(thePaintTask).IToolText(SELF, itsBitMap);
  228.                 end;
  229.  
  230.             toolRRECT: 
  231.                 begin
  232.                     new(CToolRRect(thePaintTask));
  233.                     CToolRRect(thePaintTask).IToolShapes(SELF, itsBitMap);
  234.                 end;
  235.  
  236.             toolFILLRRECT: 
  237.                 begin
  238.                     new(CToolFillRRect(thePaintTask));
  239.                     CToolFillRRect(thePaintTask).IToolShapes(SELF, itsBitMap);
  240.                 end;
  241.  
  242.             toolLINE: 
  243.                 begin
  244.                     new(CToolLine(thePaintTask));
  245.                     CToolLine(thePaintTask).IToolShapes(SELF, itsBitMap);
  246.                 end;
  247.  
  248.             toolOVAL: 
  249.                 begin
  250.                     new(CToolOval(thePaintTask));
  251.                     CToolOval(thePaintTask).IToolShapes(SELF, itsBitMap);
  252.                 end;
  253.  
  254.             toolFILLOVAL: 
  255.                 begin
  256.                     new(CToolFillOval(thePaintTask));
  257.                     CToolFillOval(thePaintTask).IToolShapes(SELF, itsBitMap);
  258.                 end;
  259.  
  260.             otherwise
  261.                 ;
  262.         end;
  263.  
  264.         theBounds := bounds;            { Mouse down drawing within the    }
  265.                                     {   bounds of the painting                }
  266.  
  267.         TrackMouse(thePaintTask, hitPt, theBounds);
  268.  
  269.         if select then                 { Selection is not undoable so we    }
  270.             thePaintTask.Free            {   don't need the task anymore        }
  271.         else
  272.             begin
  273.                 if notifiable then             { Make sure we don't re-notify in    }
  274.                                         {   the case of a resumed drag        }
  275.  
  276.                     itsSupervisor.Notify(thePaintTask);
  277.  
  278.                 lastTask := thePaintTask;    { Save task for later reference    }
  279.             end;
  280.     end;
  281.  
  282.  
  283. {****************************************************}
  284. { AdjustCursor (OVERRIDE)}
  285. {}
  286. {        Adjust the shape of the cursor according the position of the mouse.}
  287. {}
  288. {****************************************************}
  289.  
  290.     procedure CPaintPane.AdjustCursor (where: Point; mouseRgn: RgnHandle);
  291.         var
  292.             mouseRect: Rect;
  293.  
  294.     begin
  295.         WindToFrame(where);
  296.         if not PtInRect(where, bounds) then
  297.             begin
  298.                 SetCursor(arrow);                { Mouse is outside the drawing    }
  299.                 mouseRect := bounds;            {   area                            }
  300.                 FrameToGlobalR(mouseRect);
  301.                 RectRgn(gUtilRgn, mouseRect);
  302.                 DiffRgn(mouseRgn, gUtilRgn, mouseRgn);
  303.                 Exit(AdjustCursor);
  304.             end;
  305.  
  306.         case gPaintTool of                { Cursor shape depends on tool        }
  307.  
  308.             toolSELECT: 
  309.                 if PtInRect(where, selRect) then
  310.                     SetCursor(gDragCurs^^)
  311.                 else
  312.                     SetCursor(gSelectCurs^^);
  313.  
  314.             toolBRUSH: 
  315.                 SetCursor(gBrushCurs^^);
  316.  
  317.             toolPENCIL: 
  318.                 SetCursor(gPencilCurs^^);
  319.  
  320.             toolERASER: 
  321.                 SetCursor(gEraserCurs^^);
  322.  
  323.             toolTEXT: 
  324.                 SetCursor(gIBeamCursor^^);
  325.  
  326.             toolRECT, toolFILLRECT, toolRRECT, toolFILLRRECT, toolLINE, toolOVAL, toolFILLOVAL: 
  327.                 SetCursor(gShapeCurs^^);
  328.  
  329.             otherwise
  330.                 ;
  331.         end;
  332.  
  333.         if not EmptyRect(selRect) then
  334.             begin                                { Cursor changes shape inside    }
  335.                 mouseRect := selRect;            {   selection rectangle            }
  336.                 FrameToGlobalR(mouseRect);
  337.                 RectRgn(gUtilRgn, mouseRect);
  338.                 if PtInRect(where, selRect) then
  339.                     SectRgn(mouseRgn, gUtilRgn, mouseRgn)
  340.                 else
  341.                     DiffRgn(mouseRgn, gUtilRgn, mouseRgn);
  342.             end;
  343.  
  344.         mouseRect := bounds;                    { In case frame is bigger than    }
  345.         FrameToGlobalR(mouseRect);            {   bounds                            }
  346.         RectRgn(gUtilRgn, mouseRect);
  347.         SectRgn(mouseRgn, gUtilRgn, mouseRgn);
  348.     end;
  349.  
  350.  
  351. {****************************************************}
  352. { DoKeyDown (OVERRIDE)}
  353. {}
  354. {        Hitting the backspace/delete key clears the current selection}
  355. {}
  356. {****************************************************}
  357.  
  358.     procedure CPaintPane.DoKeyDown (theChar: char; keyCode: Byte; macEvent: EventRecord);
  359.     begin
  360.  
  361.         if (theChar = chr(DELETE_KEY)) and not EmptyRect(selRect) then
  362.             DoClear(iCLEAR)
  363.  
  364.         else
  365.             inherited DoKeyDown(theChar, keyCode, macEvent);
  366.  
  367.     end;
  368.  
  369.  
  370. {****************************************************}
  371. { Dawdle (OVERRIDE)}
  372. {}
  373. {        Animate the selection rectangle during Idle time}
  374. {}
  375. {****************************************************}
  376.  
  377.     procedure CPaintPane.Dawdle (var maxSleep: longint);
  378.         var
  379.             thePat: Pattern;
  380.             ticks: longint;
  381.             sRect: Rect;
  382.  
  383.     begin
  384.         if not EmptyRect(selRect) then
  385.             begin                                { Make sure selection exists            }
  386.                 sRect := selRect;
  387.                 Prepare;
  388.  
  389.                 PenNormal;                        { Draw rectangle with one pattern    }
  390.                 GetIndPattern(thePat, PAT_MARCH_ANTS, 1);
  391.                 PenPat(thePat);
  392.                 FrameRect(sRect);
  393.  
  394.                 Delay(6, ticks);            { Wait a while                            }
  395.  
  396.                                     { Redraw with a pattern offset from    }
  397.                                     {   the first. This gives the                }
  398.                                     {   illusion of marching ants.                }
  399.  
  400.                 GetIndPattern(thePat, PAT_MARCH_ANTS, 2);
  401.                 PenPat(thePat);
  402.                 FrameRect(sRect);
  403.                 maxSleep := 5;
  404.             end;
  405.     end;
  406.  
  407.  
  408. {****************************************************}
  409. { ChangeSize}
  410. {}
  411. {        Change the size of a PaintPane. If necessary, shift painting so}
  412. {        that it always completely covers the frame.}
  413. {}
  414. {****************************************************}
  415.  
  416.     procedure CPaintPane.ChangeSize (delta: Rect; redraw: Boolean);
  417.         var
  418.             hShift, vShift: integer;
  419.  
  420.     begin
  421.                                         { If new size would cause the        }
  422.                                         {   frame to extend beyond the    }
  423.                                         {   bounds of the painting, we        }
  424.                                         {   have to shift the painting.        }
  425.  
  426.         hShift := Max(frame.right + delta.right - bounds.right, 0);
  427.         vShift := Max(frame.bottom + delta.bottom - bounds.bottom, 0);
  428.  
  429.         inherited ChangeSize(delta, redraw);
  430.  
  431.         if (hShift > 0) or (vShift > 0) then
  432.             begin
  433.                 Scroll(-hShift, -vShift, false);
  434.                 if redraw then
  435.                     Refresh;
  436.             end;
  437.     end;
  438.  
  439.  
  440. {****************************************************}
  441. { KillSelRect}
  442. {}
  443. {        Get rid of selection rectangle and finalize the last task}
  444. {}
  445. {****************************************************}
  446.  
  447.     procedure CPaintPane.KillSelRect;
  448.         var
  449.             sRect: Rect;
  450.  
  451.     begin
  452.         if lastTask <> nil then
  453.             begin                        { Killing selection means we are about    }
  454.                 lastTask.DoTask;            {   to do something new. Let the last    }
  455.                 lastTask := nil;            {   task finalize its actions.                }
  456.             end;
  457.         sRect := selRect;                    { Set empty selection and force redraw    }
  458.         SetRect(selRect, 0, 0, 0, 0);    {   of old selection to clear out the            }
  459.         DrawAll(sRect);                    {   marching ants                                }
  460.     end;
  461.  
  462.  
  463. {****************************************************}
  464. { Deactivate (OVERRIDE)}
  465. {}
  466. {        Kill selection when deactivated}
  467. {}
  468. {****************************************************}
  469.  
  470.     procedure CPaintPane.Deactivate;
  471.     begin
  472.         inherited Deactivate;
  473.  
  474.         if ReallyVisible then     { Don't bother to kill selection if    }
  475.             KillSelRect;                {   Pane is not visible                    }
  476.     end;
  477.  
  478.  
  479. {****************************************************}
  480. { DoCopy}
  481. {}
  482. {        Copy selection to the clipboard. This action is NOT undoable.}
  483. {}
  484. {****************************************************}
  485.  
  486.     procedure CPaintPane.DoCopy;
  487.         var
  488.             thePic: PicHandle;
  489.             theSelRect: Rect;
  490.  
  491.     begin
  492.         itsBitMap.BeginDrawing;
  493.         theSelRect := selRect;
  494.                                     { Open picture to store selection    }
  495.  
  496.         thePic := OpenPicture(theSelRect);
  497.  
  498.                                     { Draw selection on top of itself    }
  499.  
  500.         itsBitMap.CopyFrom(theSelRect, theSelRect, nil);
  501.         ClosePicture;
  502.         itsBitMap.EndDrawing;
  503.  
  504.                                     { Copy picture to the clipboard    }
  505.  
  506.         gClipboard.PutData('PICT', Handle(thePic));
  507.         KillPicture(thePic);
  508.     end;
  509.  
  510.  
  511. {****************************************************}
  512. { DoPaste}
  513. {}
  514. {        Paste picture from the clipboard. This action isn't truly}
  515. {        undoable. We fool the pane into thinking that a drag has}
  516. {        occurred.}
  517. {}
  518. {****************************************************}
  519.  
  520.     procedure CPaintPane.DoPaste;
  521.         var
  522.             thePic: PicHandle;
  523.             anyPt: Point;
  524.             pFrame: Rect;
  525.             theLastTask: CPaintTask;        { Altered by TCL Weaver 1.0 (5/9/90) }
  526.  
  527.     begin
  528.         if gClipboard.GetData('PICT', Handle(thePic)) then
  529.             begin
  530.                                         { Set tool to selection rectangle        }
  531.                                         {   so pasted image can be dragged    }
  532.  
  533.                 DoCommand(-(BSL(MENUtools, 16) + integer(toolSELECT)));
  534.  
  535.                                         { Paste is treated as a drag in    }
  536.                                         {   progress                        }
  537.  
  538.                 new(CDragger(theLastTask));     { Altered by TCL Weaver 1.0 (5/9/90) }
  539.                 lastTask := theLastTask;
  540.                 CDragger(lastTask).IDragger(SELF, itsBitMap);
  541.  
  542.                                             { Place pasted picture near the    }
  543.                                             {   top left of the pane            }
  544.                 selRect := thePic^^.picFrame;
  545.                 OffsetRect(selRect, frame.left + 36 - selRect.left, frame.top + 36 - selRect.top);
  546.  
  547.                                             { Save bits under the paste        }
  548.  
  549.                 pFrame := selRect;
  550.                 CopyBits(itsBitMap.macBitMap, gScratchPad.macBitMap, pFrame, pFrame, srcCopy, nil);
  551.  
  552.                 itsBitMap.BeginDrawing;            { Draw pasted image on painting    }
  553.                 DrawPicture(thePic, pFrame);
  554.                 itsBitMap.EndDrawing;
  555.  
  556.                 anyPt := topLeft(pFrame);        { Fake out Dragger into thinking    }
  557.                                             {   a selection has been dragged    }
  558.                 lastTask.BeginTracking(anyPt);
  559.  
  560.                 CopyBits(gScratchPad.macBitMap, itsBitMap.macBitMap, pFrame, pFrame, srcCopy, nil);
  561.  
  562.                 lastTask.EndTracking(anyPt, anyPt, anyPt);
  563.  
  564.                 RefreshRect(pFrame);            { Force redraw and proceed as    }
  565.                 gSleepTime := 0;                    {   a suspended drag                }
  566.                 itsSupervisor.Notify(lastTask);
  567.                 KillPicture(thePic);
  568.             end;
  569.     end;
  570.  
  571.  
  572. {****************************************************}
  573. { DoClear}
  574. {}
  575. {        Erase the current selection}
  576. {}
  577. {****************************************************}
  578.  
  579.     procedure CPaintPane.DoClear (taskIndex: integer);
  580.         var
  581.             theClear: CClearTask;
  582.  
  583.     begin                                { Can't get much simpler:        }
  584.                                         {   Create a task, do it, and tell    }
  585.                                         {   supervisor it's been done        }
  586.         new(theClear);
  587.         theClear.IClearTask(taskIndex, SELF, itsBitMap);
  588.         theClear.DoTask;
  589.         itsSupervisor.Notify(theClear);
  590.         lastTask := theClear;
  591.     end;
  592.  
  593.  
  594. end.